﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace iMario
{
    public class Character : Sprite2D
    {
        //Mục đích class Character:
        //  - Biểu diễn và thực hiện các xử lí liên quan đến Character - Mario

        //Danh sách các thuộc tính và ý nghĩa
        static public String ClassName = "Character";       //Tên lớp đối tượng

        private int _heighJump;                             //Độ cao khi nhảy hiện tại của nhân vật, tính từ vị trí đang đứng và nhảy lên
        public int heighJump
        {
            get { return _heighJump; }
            set { _heighJump = value; }
        }

        private bool _isJumping;                            //Cờ hiệu cho biết nhân vật có đang nhảy lên hay không
        public bool isJumping
        {
            get { return _isJumping; }
            set { _isJumping = value; }
        }

        private List<Shot> _shots;                          //Danh sách các viên đạn do nhân vật bắn ra
        public List<Shot> shots
        {
            get { return _shots; }
        }

        private int _way;                                   //Giá trị cho biết đối tượng đang được vẽ theo loại sprite nào
        public int way
        {
            get { return _way; }
            set { _way = value; }
        }

        private int _maxHeighJump;                          //Lưu giá trị độ cao tối đa của nhân vật mỗi khi nhảy, tính từ vị trí bắt đầu nhảy
        private bool _isFalling;                            //Giá trị cờ hiệu cho biết nhân vật đang ở trạng thái rơi trong khi nhảy
        private bool _isMoving;                             //Giá trị cờ hiệu cho biết nhân vật có đang ở trạng thái di chuyện không
        

        //Danh sách các phương thức và ý nghĩa
        //Hàm dựng 
        //  1. Tham số truyền vào:
        //      - String _nameKindOfSprite: Tên của loại sprite nhân vật thuộc về
        //      - int _nSprite: Số texture2D của loại sprite của nhân vật
        //      - Point _inputSize: kích thước của nhân vật trên màn hình game, với x là chiều rộng và y là chiều cao
        //  2. Giá trị trả về: Không có
        public Character()
        {
            //Hướng xử lí
            //  Thiết lập giá trị cho các thuộc tính của đối tượng


            //Tiến hành
            //  Thiết lập giá trị cho các thuộc tính của đối tượng
            _heighJump = 0;

            _isJumping = false;

            _isFalling = true;

            _isMoving = false;

            _way = KindOfSprite.RIGHT;

            _shots = new List<Shot>();

            isActive = true;

            isDead = false;

            kind = null;

            iTexture = 0;

            iUpdate = 1;
            nUpdate = 2;

            blood = 100;

            graphicLocation = new Point(0, 100);

            //mapManager = null;
        }

        //Hàm nhân bản thêm 1 đối tượng thuộc lớp Character và trả về chính đối tượng đã được nhân bản thành công đó
        //  1. Tham số truyền vào: Không có
        //  2. Giá trị trả về:
        //      - Con trỏ GameEntity của đối tượng đã được nhân bản thành công
        public override GameEntity Clone()
        {
            GameObject character = new Character();

            ((Character)character).isActive = true;
            ((Character)character).isDead = false;
            ((Character)character).iTexture = this.iTexture;
            ((Character)character).iUpdate = this.iUpdate;
            ((Character)character).nUpdate = this.nUpdate;
            ((Character)character).kind = this.kind;
            ((Character)character).way = this.way;
            ((Character)character).graphicLocation = new Point(this.graphicLocation.X, 0);
            ((Character)character).isJumping = false;
            ((Character)character)._isFalling = true;

            return ((GameObject)character);
        }

        //Hàm cập nhật trạng thái cho đối tượng thuộc lớp Character
        //  1. Tham số truyền vào:
        //      - KeyboardState _keyboard: Đại diện cho trạng thái của Keyboard
        //      - MouseState _mouse: Đại diện cho trạng thái của Mouse
        //      - GameTime _gameTime: biến đếm thời gian của game
        //  2. Giá trị trả về: Không có
        public override void Update(KeyboardState _keyboard, MouseState _mouse, GameTime _gameTime)
        {
            //Hướng xử lí
            //Kiểm tra: Nếu chu kì lặp của game đã trùng với chu kì update của đối tượng => Thực hiện tiếp các lệnh bên dưới
            //Thiết lập giá trị cờ hiệu cho _isMoving thành false
            //Kiểm tra và xử lí trạng thái độ cao hiện tại của nhân vật
            //  Nếu nhân vật đang thực hiện quá trình nhảy <=> _isJumping == true
            //Xét từng trường hợp của bản phím thông qua biến _keyboard
            //  1. Nếu IsKeyDown(Keys.Left) == true
            //      Dịch chuyển nhân vật sang trái
            //  2. Nếu IsKeyDown(Keys.Right) == true
            //      Dịch chuyển nhân vật sang phải
            //  3. Nếu IsKeyDown(Keys.Up) == true
            //      Dịch chuyển nhân vật lên trên


            //Tiến hành
            //Kiểm tra: Nếu chu kì lặp của game đã trùng với chu kì update của đối tượng => Thực hiện tiếp các lệnh bên dưới
            if (iUpdate != nUpdate)
            {
                iUpdate++;
                return;
            }

            iUpdate = 1;

            //Thiết lập giá trị cờ hiệu cho _isMoving thành false
            _isMoving = false;

            //Kiểm tra và xử lí trạng thái độ cao hiện tại của nhân vật
            //Nếu nhân vật đang ở trong trạng thái nhảy
            if (_isJumping == true)
            {
                //Nếu nhân vật đang ở trong giai đoạn nhảy lên
                if (_isFalling == false)
                {
                    //Nếu nhân vật không bị đụng độ bên trên với bản đồ và không vượt quá chiều cao tối đa của lần nhảy => Thực hiện tiếp các lệnh bên dưới
                    if ((Collision(new Rectangle(mapManager.currentMap.mapBegin.X + graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y),
                              mapManager.currentMap,
                              GameObject.UP) == false)
                        && (_graphicLocation.Y >= _maxHeighJump))
                    {
                        //Gọi hàm giảm tung độ của nhân vật
                        AddGraphicLocation(0, -kind.speed.Y);

                        //Thiết lập giá trị iTexture
                        iTexture = 1;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.LEFT)
                            _way = KindOfSprite.JUMPLEFT;
                        else if (_way == KindOfSprite.RIGHT)
                            _way = KindOfSprite.JUMPRIGHT;
                    }
                    else    //Nếu nhân vật bị đụng độ phía trên
                    {
                        //Thiết lập giá trị cho cờ hiệu _isFalling thành true
                        _isFalling = true;

                        //Thiết lập giá trị iTexture
                        iTexture = 2;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.LEFT || _way == KindOfSprite.JUMPLEFT)
                            _way = KindOfSprite.JUMPLEFT;
                        else if (_way == KindOfSprite.RIGHT || _way == KindOfSprite.JUMPRIGHT)
                            _way = KindOfSprite.JUMPRIGHT;
                    }
                }
                else     //_isFalling = true <=> Nhân vật đang ở trong giai đoạn nhảy xuống
                {
                    //Kiểm tra đụng độ phía dưới giữa nhân vật và bản đồ
                    if (Collision(new Rectangle(mapManager.currentMap.mapBegin.X + graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y),
                                  mapManager.currentMap,
                                  GameObject.DOWN) == false)
                    {
                        //Không xảy ra đụng độ
                        //Tăng tung độ của character lên
                        AddGraphicLocation(0, Gravity);

                        //Thiết lập giá trị cho iTexture
                        iTexture = 2;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.LEFT || _way == KindOfSprite.JUMPLEFT)
                            _way = KindOfSprite.JUMPLEFT;
                        else if (_way == KindOfSprite.RIGHT || _way == KindOfSprite.JUMPRIGHT)
                            _way = KindOfSprite.JUMPRIGHT;
                    }
                    else    //Nếu nhân vật bị đụng độ phía dưới với bản đồ
                    {
                        //Thiết lập giá trị cho cờ hiệu _isFalling thành true
                        _isFalling = true;
                        
                        //Thiết lập giá trị cho cờ hiệu _isJumping thành false
                        _isJumping = false;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.JUMPLEFT)
                            _way = KindOfSprite.LEFT;
                        else if (_way == KindOfSprite.JUMPRIGHT)
                            _way = KindOfSprite.RIGHT;

                        //Thiết lập giá trị cho iTexture
                        iTexture = 0;
                    }
                }
            }
            else    //_isJumping = false <=> Nhân vật không đang ở trong trạng thái nhảy
            {
                //Nếu nhân vật đang ở trong giai đoạn rớt xuống
                if (_isFalling == true)
                {
                    //Kiểm tra đụng độ phía dưới giữa nhân vật và bản đồ
                    if (Collision(new Rectangle(mapManager.currentMap.mapBegin.X + graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y),
                                  mapManager.currentMap,
                                  GameObject.DOWN) == false)
                    {
                        //Không xảy ra đụng độ
                        //Tăng tung độ của character lên
                        AddGraphicLocation(0, Gravity);

                        //Thiết lập giá trị cho cờ hiệu _isJumping thành true
                        _isJumping = true;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.LEFT)
                            _way = KindOfSprite.JUMPLEFT;
                        else if (_way == KindOfSprite.RIGHT)
                            _way = KindOfSprite.JUMPRIGHT;

                        //Thiết lập giá trị cho iTexture
                        iTexture = 2;
                    }
                    else    //Nếu bị đụng độ
                    {
                        //Thiết lập giá trị cho cờ hiệu _isFalling thành false
                        _isFalling = true;

                        //Thiết lập giá trị cho cờ hiệu _isJumping thành false
                        _isJumping = false;

                        //Thiết lập giá trị cho _way
                        if (_way == KindOfSprite.JUMPLEFT)
                            _way = KindOfSprite.JUMPLEFT;
                        else if (_way == KindOfSprite.JUMPRIGHT)
                            _way = KindOfSprite.JUMPRIGHT;
                    }
                }
            }

            //  Xét từng trường hợp của bản phím thông qua biến _keyboard
            //  1. Nếu IsKeyDown(Keys.Left) == true
            if (_keyboard.IsKeyDown(Keys.Left))
            {               
                //      Dịch chuyển nhân vật sang trái
                //Kiểm tra: Nếu không xảy ra đụng độ phía bên trái giữa nhân vật và map => thực hiện tiếp các câu lệnh bên dưới
                if (Collision(new Rectangle(mapManager.currentMap.mapBegin.X + graphicLocation.X - 3, graphicLocation.Y, kind.size.X, kind.size.Y),
                              mapManager.currentMap,
                              GameObject.LEFT) == false)
                {
                    //Thiết lập giá trị cờ hiệu cho _isMoving thành true
                    _isMoving = true;

                    //Thiết lập giá trị cho _way
                    _way = KindOfSprite.LEFT;

                    //Nếu mapManager.currentMap.mapBegin.X thuộc khoảng (0; (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize)
                    if ((mapManager.currentMap.mapBegin.X > 0) && (mapManager.currentMap.mapBegin.X <= (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize))
                    {
                        //Gọi hàm update dành cho bản đồ
                        mapManager.Update(_keyboard, _mouse, _gameTime);
                    }
                    else
                    {
                        //Nếu mapManager.currentMap.mapBegin.X == 0
                        //  hoặc mapManager.currentMap.mapBegin.X >= (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize
                        if ((mapManager.currentMap.mapBegin.X == 0)
                            || (mapManager.currentMap.mapBegin.X >= (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize))
                            //Gọi hàm update dành cho nhân vật
                            AddGraphicLocation(-kind.speed.X, 0);
                    }                    
                }

                if (_isMoving == true)
                {
                    iTexture++;
                    if (iTexture >= kind.spriteLeft.Count)
                        iTexture = 0;
                }
                else
                    iTexture = 0;
            }

            //  2. Nếu IsKeyDown(Keys.Right) == true
            if (_keyboard.IsKeyDown(Keys.Right))
            {            
                //      Dịch chuyển nhân vật sang phải
                //Kiểm tra: Nếu không xảy ra đụng độ => thực hiện các lệnh trong hàm if phía dưới
                if (Collision(new Rectangle(mapManager.currentMap.mapBegin.X + graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y),
                              mapManager.currentMap,
                              GameObject.RIGHT) == false)
                {
                    //Thiết lập giá trị cờ hiệu cho _isMoving thành true
                    _isMoving = true;

                    //Thiết lập giá trị cho _way
                    _way = KindOfSprite.RIGHT;

                    //Nếu (hoành độ nhân vật trên màn hình thuộc khoảng [0; 400)
                    //hoặc (mapManager.currentMap.mapBegin.X >= (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize)
                    if ((graphicLocation.X >= 0 && graphicLocation.X < 400)
                        || (mapManager.currentMap.mapBegin.X == (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize))
                    {
                        //Thực hiện update vị trí trên màn hình game cho nhân vật
                        AddGraphicLocation(kind.speed.X, 0);

                        //Không gọi hàm Update của bản đồ
                    }
                    else
                    {
                        //Nếu  graphicLocation.X nằm ngoài khoàng [0; 400) 
                        //và mapManager.currentMap.mapBegin.X thuộc khoảng [0; (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize)
                        if ((graphicLocation.X >= 400)
                            && (mapManager.currentMap.mapBegin.X >= 0)
                            && (mapManager.currentMap.mapBegin.X < (Map.MapWidth - 1 - Map.WindowWidth / Map.TileSize - 1) * Map.TileSize))
                        {
                            //Thực hiện update cho bản đồ
                            mapManager.Update(_keyboard, _mouse, _gameTime);
                        }
                    }                    
                }

                if (_isMoving == true)
                {
                    iTexture++;
                    if (iTexture >= kind.spriteRight.Count)
                        iTexture = 0;
                }
                else
                    iTexture = 0;
            }

            //  3. Nếu IsKeyDown(Keys.Up) == true
            if (_keyboard.IsKeyDown(Keys.Up))
            {
                //      Dịch chuyển nhân vật lên trên
                //Kiểm tra: nếu nhân vật đang ở trong trạng thái _isJumping = false => Thực hiện các bước xử lí tiếp theo
                if (_isJumping == false)
                {
                    //Thiết lập giá trị cờ hiệu cho _isJumping thành true
                    _isJumping = true;

                    //Thiết lập giá trị cờ hiệu cho _isFalling thành false
                    _isFalling = false;

                    //Thiết lập độ cao tối đa nhân vật được nhảy với độ cao hiện tại của nhân vật
                    _maxHeighJump = _graphicLocation.Y - kind.maxHeighJump;

                    //Thiết lập giá trị cho _way
                    if (_way == KindOfSprite.LEFT)
                        _way = KindOfSprite.JUMPLEFT;
                    else if (_way == KindOfSprite.RIGHT)
                        _way = KindOfSprite.JUMPRIGHT;
                }
            }

            if (_isMoving == false)
                iTexture = 0;
        }

        //Hàm xử lí thao tác vẽ đối tượng lên màn hình game
        //  1. Tham số truyền vào
        //      - SpriteBatch _spriteBatch
        //      - GameTime _gameTime: dùng trong việc đếm số chu kì cập nhật trạng thái và vẽ lại đối tượng trên màn hình game
        public override void Draw(SpriteBatch _spriteBatch, GameTime _gameTime)
        {
            //Hướng xử lí
            //Kiểm tra: Nếu kind != null => thực thi các lệnh bên dưới
            //  Xét từng trường hợp giá trị  của _way
            //  Thực hiện vẽ đối tượng kind tại client xác định


            //Tiến hành
            //Kiểm tra: Nếu kind != null => thực thi các lệnh bên dưới
            if (kind == null)
                return;
            
            //  Xét từng trường hợp giá trị  của _way
            //  Thực hiện vẽ đối tượng kind tại client xác định
            switch (_way)
            {
                case KindOfSprite.LEFT:
                    {
                        kind.Draw(_spriteBatch, iTexture, new Rectangle(graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y), KindOfSprite.LEFT);

                        break;
                    }
                case KindOfSprite.RIGHT:
                    {
                        kind.Draw(_spriteBatch, iTexture, new Rectangle(graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y), KindOfSprite.RIGHT);

                        break;
                    }
                case KindOfSprite.JUMPLEFT:
                    {
                        kind.Draw(_spriteBatch, iTexture, new Rectangle(graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y), KindOfSprite.JUMPLEFT);

                        break;
                    }
                case KindOfSprite.JUMPRIGHT:
                    {
                        kind.Draw(_spriteBatch, iTexture, new Rectangle(graphicLocation.X, graphicLocation.Y, kind.size.X, kind.size.Y), KindOfSprite.JUMPRIGHT);

                        break;
                    }
            }
        }

        //Hàm thay đổi loại sprite (loại character, loại monster, loại đạn)
        //  1. Tham số truyền vào:
        //      - KindOfSprite _kindOfSprite: Đại diện cho loại sprite đối tượng character này thuộc về
        //  2. Giá trị trả về: Không có
        public void SetKindOfSprite(KindOfSprite _kindOfSprite)
        {
            kind = _kindOfSprite;
        }

        //Hàm xử lí khi character bị chết
        //  1. Tham số truyền vào: Không có
        //  2. Giá trị trả về: Không có
        public void Dead()
        {
            //Tạm thời chưa xử lí            
        }
    }
}
